<?php

/**
  * Task Transforms a Dia UML to a propel schema
  *
  * @author  Gabor Kormany - <kormanyg@gmail.com>
  * @version 0.2.1
   */

class DiaToPropelTask extends sfPropelBaseTask
{
  // Default env options
  const DEFAULT_ENV_OPTION        = 'cli';
  const DEFAULT_DEBUG_OPTION      = true;

  // Default task options
  const DEFAULT_FILE_DIR_OPTION        = 'config';
  const DEFAULT_OUTPUT_DIR_OPTION      = '/config';
  const DEFAULT_PACKAGE_OPTION         = 'lib.model';
  const DEFAULT_EXTERNAL_TABLES_OPTION = '';

  /**
   * @see sfTask
   */
  protected function configure()
  {
    $this->addArguments(array(
      new sfCommandArgument( 'file', sfCommandArgument::REQUIRED, 'The file name'),
    ));

    $this->addOptions(array(
      new sfCommandOption( 'y', null, sfCommandOption::PARAMETER_OPTIONAL, 'Outputs yml(s) instead of xml(s)', 0 ),
      new sfCommandOption( 's', null, sfCommandOption::PARAMETER_OPTIONAL, 'Split schema into tables', 0 ),
    ));

    $this->namespace = 'propel';
    $this->name = 'dia-to-propel';
    $this->briefDescription = 'Transforms a Dia XML schema to a valid Propel schema file';

    $this->detailedDescription = <<<EOF
The [propel:dia-to-propel|INFO] transforms a Dia XML schema to propel schema files, one table_name-schema.xml for every table in the DB.

Only the visible layers are parsed!

  Quick version:
  --> [./symfony propel:dia-to-propel db_model.dia|INFO]
EOF;
  }

  /**
   * @see sfTask
   */
  protected function execute($arguments = array(), $options = array())
  {
    // Check parameters

    $this->setClassOptions($arguments, $options);
    $this->checkParameters();

    // 0 - Start
    $this->logSection('START', ' - Start time: '. $this->getCurrentDateTime() );

    // 1 - Convert schema to xml
    $this->doConvertSchema();

    // 2 - Split schema ( optional )
    if ( $options['s'] !== 0 )
      echo $this->doSplitSchema();


    // 3 - Convert to yml
    if ( $options['y'] !== 0 )
    {
      $this->convertSchemaToYml();
      $this->clearSchemaXmls();
    }

    // 4 - End
    $this->logSection('END', ' - End time: '. $this->getCurrentDateTime());
  }

  /**
   * Check parameters and raise at the first incorrect one.
   *
   * @return Exception
   */
  protected function checkParameters($arguments = array(), $options = array())
  {
    // Check DB4 input file
    $this->file_path = sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.$this->file;

    if (!file_exists($this->file_path) || !is_readable($this->file_path))
    {
      throw new Exception(sprintf('The dia file can\'t be found at %s, please check the path and correct the file_dir and file options.', $this->file_path));
    }

    // Check xlst
    if (!class_exists('xsltprocessor'))
    {
      throw new Exception('PHP XSL extension must be installed and enabled on your system to use this task.');
    }
  }

  /**
   * Transforms the DB4 schema to a Propel one.
   */
  function doConvertSchema()
  {
    // DIA xsl
    $dia_transformation_file = dirname(__FILE__). '/../vendor/dia/dia-uml2propel.xsl';

    chdir( dirname( $this->file ) );

    $resArch = gzopen( basename( $this->file ), "rb" );

    if ( $resArch )
    {
        $resTmp = fopen( "tmp.xml", "w" );
        while ( fputs( $resTmp, gzgets( $resArch, 4096 ) ) );
        gzclose( $resArch );
        fflush( $resTmp );
        fclose( $resTmp );


        $this->clearSchemaXmls();


        // Load the XML source
        $xml = new DOMDocument;
        $xml->load('tmp.xml');

        $xsl = new DOMDocument;
        $xsl->load( $dia_transformation_file );

        // Configure the transformer
        $proc = new XSLTProcessor;
        $proc->importStyleSheet($xsl); // attach the xsl rules

        $proc->transformToXML($xml);

        unlink( "tmp.xml" );
    }
  }

  function doSplitSchema()
  {
    // DIA xsl
    $dia_transformation_file = dirname(__FILE__). '/../vendor/dia/schema_splitter.xsl';


      // Load the XML source
      $xml = new DOMDocument;
      $xml->load('schema.xml');

      $xsl = new DOMDocument;
      $xsl->load( $dia_transformation_file );

      // Configure the transformer
      $proc = new XSLTProcessor;
      $proc->importStyleSheet($xsl); // attach the xsl rules

      $proc->transformToXML($xml);

      unlink( "schema.xml" );
  }

  /**
   * Transform xml schema to yml schema and remove xml one.
   */
  protected function convertSchemaToYml()
  {
    // Don't convert if the output file is not /config
    $filesystem = $this->getFilesystem();
    $cmd = 'php '. sfConfig::get('sf_root_dir'). '/symfony propel:schema-to-yml';
    $filesystem->sh($cmd);
    $this->logSection('file+', 'Database xml schema converted to yml.' );
    $this->clearSchemaXmls();
    $this->logSection('file-', 'Removed database xml schema(s)...');
  }

  /**
   * Return current formated date time to check task length.
   *
   * @return String
   */
  protected function getCurrentDateTime()
  {
    return date('Y-m-d H:i:s');
  }

  /**
   * Assign options to class to make them available everywhere.
   */
  protected function setClassOptions($arguments = array(), $options = array())
  {
    foreach ($options as $option => $value)
    {
      if (empty($option))
      {
        continue;
      }
      $this->$option = $options[$option];
    }
    $this->file = $arguments['file'];
    if ( dirname( $this->file ) == "." ) {
        $this->file = self::DEFAULT_FILE_DIR_OPTION.DIRECTORY_SEPARATOR.$this->file;
    }

  }

  protected function clearSchemaXmls()
  {
    $hDir = opendir( getcwd() );
    while( ( $filename = readdir( $hDir ) ) !== false )
    {
        if ( is_dir( $filename ) )
        {
        }
        else if ( strpos( $filename, "schema.xml" ) !== false || strpos( $filename, "schema-transformed.xml" ) !== false )
        {
            $this->logSection( 'file-', $filename );
            unlink( $filename );
        }
    }
    closedir( $hDir );
  }
}
